﻿#include <iostream>
#include <string>
#include <map>
#include <thread>
#include <mutex>
#include <fstream>
#include <sstream>
#include <vector>
#include <ctime>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <chrono> 
#pragma comment(lib, "ws2_32.lib")

using namespace std;

struct License {
    string serial;
    string username;
    string password;
    int max_users;
};

struct Session {
    string client_id;
    string serial;
    time_t last_heartbeat;
};

map<string, License> licenses;
map<string, Session> sessions;
mutex licenses_mutex;
mutex sessions_mutex;
const int TIMEOUT = 35 * 60; // 35分钟超时

void load_licenses() {
    ifstream file("licenses.txt");
    string line;
    while (getline(file, line)) {
        stringstream ss(line);
        License lic;
        getline(ss, lic.serial, ',');
        getline(ss, lic.username, ',');
        getline(ss, lic.password, ',');
        ss >> lic.max_users;
        licenses[lic.serial] = lic;
    }
}

void save_licenses() {
    ofstream file("licenses.txt");
    for (auto& [serial, lic] : licenses) {
        file << lic.serial << "," << lic.username << "," << lic.password << "," << lic.max_users << endl;
    }
}

void load_sessions() {
    ifstream file("sessions.txt");
    string line;
    time_t now = time(nullptr);
    while (getline(file, line)) {
        stringstream ss(line);
        Session sess;
        getline(ss, sess.client_id, ',');
        getline(ss, sess.serial, ',');
        ss >> sess.last_heartbeat;
        if (now - sess.last_heartbeat < TIMEOUT) {
            sessions[sess.client_id] = sess;
        }
    }
}

void save_sessions() {
    ofstream file("sessions.txt");
    for (auto& [cid, sess] : sessions) {
        file << cid << "," << sess.serial << "," << sess.last_heartbeat << endl;
    }
}

string generate_serial() {
    static mutex mtx;
    lock_guard<mutex> lock(mtx);
    string serial;
    do {
        serial = "";
        for (int i = 0; i < 10; ++i) {
            serial += '0' + rand() % 10;
        }
    } while (licenses.count(serial));
    return serial;
}

void handle_admin() {
    while (true) {
        cout << "Enter command (generate/exit): ";
        string cmd;
        getline(cin, cmd);

        if (cmd == "generate") {
            License lic;
            cout << "Username: ";
            getline(cin, lic.username);
            cout << "Password: ";
            getline(cin, lic.password);
            cout << "Max users: ";
            cin >> lic.max_users;
            cin.ignore();

            lic.serial = generate_serial();

            lock_guard<mutex> lock(licenses_mutex);
            licenses[lic.serial] = lic;
            save_licenses();
            cout << "Generated serial: " << lic.serial << endl;
        }
        else if (cmd == "exit") {
            exit(0);
        }
    }
}

void check_timeouts() {
    while (true) {
        this_thread::sleep_for(60s);
        time_t now = time(nullptr);

        lock_guard<mutex> lock1(sessions_mutex);
        lock_guard<mutex> lock2(licenses_mutex);

        vector<string> to_remove;
        for (auto& [cid, sess] : sessions) {
            if (now - sess.last_heartbeat > TIMEOUT) {
                to_remove.push_back(cid);
            }
        }

        for (auto& cid : to_remove) {
            sessions.erase(cid);
        }
        save_sessions();
    }
}

void handle_client(SOCKET client_sock) {
    char buf[1024];
    int bytes = recv(client_sock, buf, sizeof(buf), 0);
    if (bytes <= 0) {
        closesocket(client_sock);
        return;
    }

    string request(buf, bytes);
    stringstream ss(request);
    string cmd;
    getline(ss, cmd, ':');

    if (cmd == "AUTH") {
        string serial;
        getline(ss, serial);

        lock_guard<mutex> lock(licenses_mutex);
        if (!licenses.count(serial)) {
            send(client_sock, "STATUS:FAILURE:INVALID_SERIAL", 29, 0);
            return;
        }

        int count = 0;
        {
            lock_guard<mutex> slock(sessions_mutex);
            for (auto& [_, sess] : sessions) {
                if (sess.serial == serial) count++;
            }
        }

        if (count >= licenses[serial].max_users) {
            send(client_sock, "STATUS:FAILURE:MAX_USERS", 24, 0);
            return;
        }

        string cid = to_string(time(nullptr)) + "-" + to_string(rand());
        {
            lock_guard<mutex> slock(sessions_mutex);
            sessions[cid] = { cid, serial, time(nullptr) };
            save_sessions();
        }

        string response = "STATUS:SUCCESS:" + cid;
        send(client_sock, response.c_str(), response.size(), 0);
    }
    else if (cmd == "HEARTBEAT") {
        string cid, serial;
        getline(ss, cid, ':');
        getline(ss, serial);

        lock_guard<mutex> slock(sessions_mutex);
        if (sessions.count(cid)) {
            sessions[cid].last_heartbeat = time(nullptr);
            save_sessions();
            send(client_sock, "STATUS:SUCCESS", 14, 0);
        }
        else {
            send(client_sock, "STATUS:FAILURE:INVALID_CLIENT", 28, 0);
        }
    }
    else if (cmd == "EXIT") {
        string cid;
        getline(ss, cid);

        lock_guard<mutex> slock(sessions_mutex);
        if (sessions.erase(cid)) {
            save_sessions();
            send(client_sock, "STATUS:SUCCESS", 14, 0);
        }
        else {
            send(client_sock, "STATUS:FAILURE", 14, 0);
        }
    }

    closesocket(client_sock);
}

int main() {
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);

    load_licenses();
    load_sessions();

    thread admin_thread(handle_admin);
    thread timeout_thread(check_timeouts);

    SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(12345);

    bind(listen_sock, (sockaddr*)&addr, sizeof(addr));
    listen(listen_sock, SOMAXCONN);

    while (true) {
        SOCKET client = accept(listen_sock, nullptr, nullptr);
        thread(handle_client, client).detach();
    }
}